﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using AjaxControlToolkit;

namespace AutoCompleteControl
{
    
    [AspNetHostingPermission(SecurityAction.Demand,
        Level = AspNetHostingPermissionLevel.Minimal),
    AspNetHostingPermission(SecurityAction.InheritanceDemand,
        Level = AspNetHostingPermissionLevel.Minimal),
    ToolboxData("<{0}:AutoCompleteControl runat=\"server\"> </{0}:AutoCompleteControl>")
        ]
    public class AutoCompleteControl : WebControl
    {
        #region .NET Controls

        private readonly UpdatePanel controlUpdatePanel = new UpdatePanel();
        private readonly PlaceHolder suggestControlPlaceHolder = new PlaceHolder();
        private readonly Panel controlContainerPanel = new Panel();
        private readonly Literal scriptsLiteral = new Literal();
        private readonly Panel suggestorPanel = new Panel();
        private Label slt = new Label();
        private readonly HiddenField valuesHiddenField = new HiddenField();
        private readonly HiddenField newValuesHiddenField = new HiddenField();
        private readonly Panel boxPanel = new Panel();
        private readonly Panel ulContainerPanel = new Panel();
        private readonly HtmlGenericControl myUl = new HtmlGenericControl("ul");
        private readonly Literal liLiteral = new Literal();
        private readonly Panel toolBoxPanel = new Panel();
        private readonly Panel suggestBoxPanel = new Panel();
        private readonly TextBox keyWordTextBox = new TextBox();
        private readonly TextBoxWatermarkExtender keyWordTextBox_TextBoxWatermarkExtender = new TextBoxWatermarkExtender();
        private readonly AutoCompleteExtender keyWordTextBox_AutoCompleteExtender = new AutoCompleteExtender();
        private readonly Panel addExtraItemPanel = new Panel();
        private readonly Label addTxtLabel = new Label();
        private readonly Button postBackButton = new Button();
        private readonly Button revPostBackButton = new Button();
        private readonly Literal keyboardLiteral = new Literal();
        private readonly Literal clearLiteral1 = new Literal();
        private readonly Literal clearLiteral2 = new Literal();
        private readonly Literal clearLiteral3 = new Literal();
        private readonly Panel buttonsPanel = new Panel();

        #endregion

        private int LastUsedWordCount { get; set; }
        public static List<Word> DirtyWords { get; set; }
        private bool HaveDirtyWord { get; set; }

        #region CODE BEHIND - PROPERTIES


        private string _selecteditem;
        [Browsable(false)]
        public string SelectedItem
        {
            get
            {
                EnsureChildControls();
                return _selecteditem;
            }
            private set
            {
                EnsureChildControls();
                _selecteditem = value;
            }
        }


        private List<string> _addedvalues;
        [Browsable(false)]
        public List<string> AddedValues
        {
            get
            {
                EnsureChildControls();
                return _addedvalues ?? (_addedvalues = new List<string>());
            }
            internal set
            {
                EnsureChildControls();
                _addedvalues = value;
            }
        }


        private List<object> _values;
        [Browsable(false)]
        public List<object> Values
        {
            get
            {
                EnsureChildControls();
                return _values ?? (_values = new List<object>());
            }
            internal set
            {
                EnsureChildControls();
                _values = value;
            }
        }

        [Browsable(false)]
        public List<int> ValuesAsInt
        {
            get
            {
                EnsureChildControls();
                try
                {
                    return Values.ConvertAll(t => int.Parse(t.ToString()));
                }
                catch
                {
                    throw new Exception("Could not parse Values (List<object>) element(s) to int.");
                }
            }
        }

        [Browsable(false)]
        public List<long> ValuesAsLong
        {
            get
            {
                EnsureChildControls();
                try
                {
                    return Values.ConvertAll(t => long.Parse(t.ToString()));
                }
                catch
                {
                    throw new Exception("Could not parse Values (List<object>) element(s) to long.");
                }
            }
        }

        [Browsable(false)]
        public List<string> ValuesAsString
        {
            get
            {
                EnsureChildControls();
                return Values.ConvertAll(t => t.ToString());
            }
        }

        #endregion

        #region PROPERTIES


        private bool _enabledirtywordfilter;
        /// <summary>
        /// If enabled, control filters all words that user newly added. 
        /// Suggestor.DirtyWords property has to be set for source of dirty words. 
        /// </summary>
        [Bindable(true), DefaultValue("false"), Category("Behavior"),
        Description("If enabled, control filters all words that user newly added. DirtyWords property has to be set for source of dirty words. ")]
        public bool EnableDirtyWordFilter
        {
            get
            {
                EnsureChildControls();
                return _enabledirtywordfilter;
            }
            set
            {
                EnsureChildControls();
                _enabledirtywordfilter = value;
            }
        }


        /// <summary>
        /// The watermark text to display on the textbox.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Appearance"), DefaultValue(""),
        Description("The watermark text to display on the textbox.")]
        public string WatermarkText
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_TextBoxWatermarkExtender.WatermarkText;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_TextBoxWatermarkExtender.WatermarkText = value;
            }
        }

        /// <summary>
        /// The text to display in add new item description box.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("press Enter now to add"),
        Description("The text to display in add new item description box.")]
        public string AddNewItemDesriptionText
        {
            get
            {
                EnsureChildControls();
                return addTxtLabel.Text;
            }
            set
            {
                EnsureChildControls();
                addTxtLabel.Text = string.IsNullOrWhiteSpace(value) ? "press Enter now to add" : value;
            }
        }


        private string _loadingimageurl;
        /// <summary>
        /// The ImageUrl of Ajax loading effect image displayed in textbox
        /// when user tries to insert a new item.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Appearance"), DefaultValue(""),
        Description("The ImageUrl of Ajax loading effect image displayed in textbox when user tries to insert a new item.")]
        public string LoadingImageUrl
        {
            get
            {
                EnsureChildControls();
                return _loadingimageurl;
            }
            set
            {
                EnsureChildControls();
                _loadingimageurl = value.Replace("~/", string.Empty);
            }
        }


        /// <summary>
        /// The Total width of the textbox of the control.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("120px"),
        Description("Sets the width of the textbox of the control.")]
        public Unit TotalWidth
        {
            private get
            {
                EnsureChildControls();
                return suggestBoxPanel.Width;
            }
            set
            {
                EnsureChildControls();
                suggestBoxPanel.Width = value;
                keyWordTextBox.Width = value;
            }
        }



        private List<Word> _lastusedwordlist;
        /// <summary>
        /// Last used word list. Words must be in this form: ID,VALUE# 
        /// ( ex: 1,microsoft#2,bing#3,asp.net# )
        /// </summary>
        [Browsable(false)]
        public List<Word> LastUsedWordList
        {
            get
            {
                EnsureChildControls();
                if (_lastusedwordlist == null)
                {
                    _lastusedwordlist = new List<Word>();
                    return _lastusedwordlist;
                }
                return _lastusedwordlist;
            }
            set
            {
                EnsureChildControls();
                _lastusedwordlist = value;
            }
        }


        private bool _insertable;
        /// <summary>
        /// Control can provide you newly added words 
        /// that not exist in your datasource.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("false")
       , Description("AutoComplete Control can insert newly added words that not exist in your datasource.")]
        public bool Insertable
        {
            get
            {
                EnsureChildControls();
                return _insertable;
            }
            set
            {
                EnsureChildControls();
                _insertable = value;
            }
        }


        private bool _multiple;
        /// <summary>
        /// Multiple item can be selected if true.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("true")
       , Description("If true, Multiple suggest item can be selected.")]
        public bool Multiple
        {
            get
            {
                EnsureChildControls();
                return _multiple;
            }
            set
            {
                EnsureChildControls();
                _multiple = value;
            }
        }


        /// <summary>
        /// ContextKey for datasourse service. 
        /// If you do not want to use, set it to 0.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("0")
       , Description("Contextkey for autocomplete control.")]
        public string ContextKey
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.ContextKey;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.ContextKey = value;
            }
        }


        /// <summary>
        /// Minimum prefix length for control.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("2")
       , Description("Minimum prefix length for control.")]
        public int MinimumPrefixLength
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.MinimumPrefixLength;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.MinimumPrefixLength = value;
            }
        }



        /// <summary>
        /// Completion interval as milisecond.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("1000")
       , Description("Completion interval as milisecond.")]
        public int CompletionInterval
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.CompletionInterval;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.CompletionInterval = value;
            }
        }


        private bool _enabletitlecasing;
        /// <summary>
        /// If true, control automatically makes uppercase newly added words.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("false")
        , Description("If true, control automatically makes uppercase newly added words.")]
        public bool EnableTitleCasing
        {
            get
            {
                EnsureChildControls();
                return _enabletitlecasing;
            }
            set
            {
                EnsureChildControls();
                _enabletitlecasing = value;
            }
        }


        private bool _showlastusedwords;
        /// <summary>
        /// If true, Control shows last used word set when loaded.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("false")
       , Description("Shows last used words list on the control.")]
        public bool ShowLastUsedWords
        {
            get
            {
                EnsureChildControls();
                return _showlastusedwords;
            }
            set
            {
                EnsureChildControls();
                if (Multiple == false && value)
                    Multiple = true;

                _showlastusedwords = value;
            }
        }


        private string _customjavafunctiononitemselected;
        /// <summary>
        /// Name of the custom javascript function that will be fired after on item selected.
        /// Function can take selected item's value and text. 
        /// Ex: MyTestFunction(itemtext, itemvalue) { alert(itemtext + ':' + itemvalue }
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("")
       , Description("Custom java function's name, it will be fired on item selected. ex: SayHi")]
        public string CustomJavascriptFunctionOnItemSelected
        {
            get
            {
                EnsureChildControls();
                return _customjavafunctiononitemselected;
            }
            set
            {
                EnsureChildControls();
                _customjavafunctiononitemselected = value;
            }
        }

        private string _customjavafunctiononitemdeleted;
        /// <summary>
        /// Name of the javascript function that will be fired after on item deleted.
        /// Function can take deleted item's value and text. 
        /// Ex: MyTestFunction(itemtext, itemvalue) { alert(itemtext + ':' + itemvalue }
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("")
       , Description("Name of the javascript function that will be fired after item deleted. ex: SayBye")]
        public string CustomJavascriptFunctionOnItemDeleted
        {
            get
            {
                EnsureChildControls();
                return _customjavafunctiononitemdeleted;
            }
            set
            {
                EnsureChildControls();
                _customjavafunctiononitemdeleted = value;
            }
        }



        /// <summary>
        /// Service Method for control.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("")
       , Description("Sets the Service Method for control.")]
        public string ServiceMethod
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.ServiceMethod;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.ServiceMethod = value;
            }
        }

        /// <summary>
        /// Service Path for control.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("")
        , Description("Sets the Service Path for control.")]
        public string ServicePath
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.ServicePath;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.ServicePath = value;
            }
        }

        /// <summary>
        /// Shown item count.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("10"),
        Description("Sets how many item shown.")]
        public int CompletionSetCount
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.CompletionSetCount;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.CompletionSetCount = value;
            }
        }



        private string _selecteditemboxcssclass = "";
        /// <summary>
        /// Css class of ul container of selected items.
        /// </summary>
        [Bindable(true), Category("Appearance"), DefaultValue(""),
        Description("Css class of ul container of selected items.")]
        public string SelectedItemBoxCssClass
        {
            get
            {
                EnsureChildControls();
                return _selecteditemboxcssclass;
            }
            set
            {
                EnsureChildControls();
                _selecteditemboxcssclass = value;
                myUl.Attributes.Add("class", _selecteditemboxcssclass);
            }
        }


        private string _selecteditemcssclass;
        /// <summary>
        /// Css class of an item which in selected box.
        /// </summary>
        [Bindable(true), Category("Appearance"), DefaultValue(""),
        Description("Css class of an item which in selected box.")]
        public string SelectedItemCssClass
        {
            get
            {
                EnsureChildControls();
                return _selecteditemcssclass;
            }
            set
            {
                EnsureChildControls();
                _selecteditemcssclass = value;
            }
        }


        private string _selecteditemondeletecssclass;
        /// <summary>
        /// Css class of an item (on pressed backspace once).
        /// </summary>
        [Bindable(true), Category("Appearance"), DefaultValue(""),
        Description("Css class of an item (on pressed backspace once).")]
        public string SelectedItemOnDeleteCssClass
        {
            get
            {
                EnsureChildControls();
                return _selecteditemondeletecssclass;
            }
            set
            {
                EnsureChildControls();
                _selecteditemondeletecssclass = string.IsNullOrWhiteSpace(value) ? _selecteditemcssclass : value;
            }
        }


        /// <summary>
        /// Completion list box Css class.
        /// </summary>
        [Bindable(true), Category("Appearance"), DefaultValue(""),
        Description("Completion list box Css class.")]
        public string ListCssClass
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.CompletionListCssClass;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.CompletionListCssClass = value;
            }
        }


        /// <summary>
        /// Completion list item Css class.
        /// </summary>
        [Bindable(true), Browsable(true), Category("Appearance"), DefaultValue(""),
        Description("Completion list item Css class.")]
        public string ListItemCssClass
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.CompletionListItemCssClass;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.CompletionListItemCssClass = value;
            }
        }


        /// <summary>
        /// Completion list highlighted item Css class.
        /// </summary>
        [Bindable(true), Category("Appearance"), DefaultValue(""),
        Description("Completion list highlighted item Css class.")]
        public string HighlightedItemCssClass
        {
            get
            {
                EnsureChildControls();
                return keyWordTextBox_AutoCompleteExtender.CompletionListHighlightedItemCssClass;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_AutoCompleteExtender.CompletionListHighlightedItemCssClass = value;
            }
        }


        [Bindable(true), Browsable(true), Category("Appearance"), DefaultValue(""),
        Description("Watermark text css class.")]
        public string WatermarkClass
        {
            private get
            {
                EnsureChildControls();
                return keyWordTextBox_TextBoxWatermarkExtender.WatermarkCssClass;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox_TextBoxWatermarkExtender.WatermarkCssClass = value;
            }
        }


        [Bindable(true), Browsable(true), Category("Appearance"), DefaultValue(""),
        Description("Textbox css class.")]
        public string TextBoxCssClass
        {
            private get
            {
                EnsureChildControls();
                return keyWordTextBox.CssClass;
            }
            set
            {
                EnsureChildControls();
                keyWordTextBox.CssClass = value;
            }
        }



        [Category("Action"), Description("Raised when the user selects an item.")]
        public event EventHandler ItemSelected;

        /// <summary>
        /// If PostBackOnItemSelected property set to true, 
        /// this event raised after an item selected. 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void ItemSelectedEvent(EventArgs e)
        {
            if (ItemSelected != null)
                ItemSelected(this, e);
        }



        [Category("Action"), Description("Raised when the user deletes an item.")]
        public event EventHandler ItemDeleted;


        /// <summary>
        /// If PostBackOnItemDeleted property set to true, 
        /// this event raised after an item selected. 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void ItemDeletedEvent(EventArgs e)
        {
            if (ItemDeleted != null)
                ItemDeleted(this, e);
        }

        #endregion


        #region METHODS

        private void ValueList()
        {
            var idList = new List<object>();
            var newEntriesIdList = new List<string>();

            var hiddenvalues = valuesHiddenField.Value;
            var arr = hiddenvalues.Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries)
                .Where(x => x.Length != 0).ToArray();

            if (arr.Length != 0)
            {
                if (arr.Count(c => c.StartsWith("?")) != 0)
                {
                    if (EnableDirtyWordFilter)
                        arr = CheckForDirtyWords(arr);
                }

                foreach (var item in arr)
                {
                    if (item.Length <= 0 || string.IsNullOrEmpty(item)) continue;
                    if (item.StartsWith("?"))
                    {
                        var newKeyword = item.Remove(0, 1).Trim().ToLower();
                        if (EnableTitleCasing)
                            newKeyword = System.Globalization.CultureInfo.CurrentUICulture
                                .TextInfo.ToTitleCase(newKeyword);

                        newEntriesIdList.Add(newKeyword);
                    }
                    else
                    {
                        idList.Add(item.Trim());
                    }
                }

                _selecteditem = arr.Last().Replace("?", "");

                AddedValues =
                    newEntriesIdList.Count == 0
                    ?
                    new List<string>() : newEntriesIdList;

                Values =
                    idList.Count == 0
                    ?
                    new List<object>() : idList.Distinct().ToList();

                valuesHiddenField.Value = string.Empty;
                newValuesHiddenField.Value = string.Empty;
            }
            else
            {
                AddedValues = new List<string>();
                Values = new List<object>();
                valuesHiddenField.Value = string.Empty;
                newValuesHiddenField.Value = string.Empty;
                _selecteditem = string.Empty;
            }
        }

        private string[] CheckForDirtyWords(IEnumerable<string> arr)
        {

            var finallist = new List<string>();

            var newwordsforcheck = arr.Where(a => a.StartsWith("?")).ToArray();
            var oldvalues = arr.Except(newwordsforcheck).ToArray();
            finallist.AddRange(oldvalues);
            var words = DirtyWords.Select(s => s.Text).ToArray();

            var isdirty = false;

            foreach (var nw in newwordsforcheck)
            {
                isdirty = false;
                var word = nw;
                foreach (var dw in words)
                {
                    if (!nw.ToLower().Substring(1).Contains(dw.ToLower())) continue;

                    isdirty = true;
                    word = word.Replace(dw.ToLower(), dw.Substring(0, 1) + "****" + dw.Substring(dw.Length - 1, 1));
                }

                if (!isdirty)
                    finallist.Add(word);
            }

            if (isdirty)
                HaveDirtyWord = true;
            return finallist.ToArray();
        }

        private void GetAndClearValues()
        {
            ValueList();
        }

        /// <summary>
        /// Updates the control.
        /// </summary>
        public void UpdateControl()
        {
            if (LastUsedWordList.Count > 0 && ShowLastUsedWords)
                GetLastUsedList();

            SetJavaFunction();

            controlUpdatePanel.Update();
        }


        public void ResetControl()
        {
            keyWordTextBox.Text = string.Empty;

            AddedValues = new List<string>();
            Values = new List<object>();
            valuesHiddenField.Value = "#";
            newValuesHiddenField.Value = "#";
            SelectedItem = string.Empty;

            UpdateControl();

            ScriptManager.RegisterClientScriptBlock(suggestorPanel, typeof(Panel), UniqueID + "_focus",
                                        string.Format("document.getElementById(\"{0}\").focus();",
                                                      keyWordTextBox.ClientID), true);
        }


        #region Embed Javascript, Css and Image (hover,out scripts) Files
        private void EmbedCssFiles()
        {
            // Add embedded style sheet to parent page
            var cssLink = new HtmlLink
            {
                ID = "SampleControlCss",
                Href = Page.ClientScript.GetWebResourceUrl(GetType(), "AutoCompleteControl.styles.css")
            };
            cssLink.Attributes.Add("rel", "stylesheet");
            cssLink.Attributes.Add("type", "text/css");
            var cssLinkAlreadyExists = false;
            foreach (Control headctrl in Page.Header.Controls)
            {
                if (headctrl.ID != cssLink.ID) continue;
                cssLinkAlreadyExists = true;
                break;
            }
            if (!cssLinkAlreadyExists)
            {
                Page.Header.Controls.AddAt(0, cssLink);
            }

        }

        private void EmbedJavaScriptFiles()
        {
            //Add embedded javascript file to the parent page
            const string resourceName = "AutoCompleteControl.jscripts.js";
            var cs = Page.ClientScript;
            cs.RegisterClientScriptResource(typeof(AutoCompleteControl), resourceName);
        }

        #endregion

        private void Java()
        {
            var sb = new StringBuilder();

            var src = string.IsNullOrEmpty(LoadingImageUrl)
                ? Page.ClientScript.GetWebResourceUrl(GetType(), "AutoCompleteControl.suggest_loader.gif")
                : LoadingImageUrl;

            sb.AppendFormat("var ctrl_{0} = new aCControl(\n", ClientID);
            sb.AppendFormat("'ctrl_{0}', \n", ClientID);
            sb.AppendFormat("'{0}', \n", keyWordTextBox.ClientID);
            sb.AppendFormat("'{0}', \n", valuesHiddenField.ClientID);
            sb.AppendFormat("'{0}', \n", newValuesHiddenField.ClientID);
            sb.AppendFormat("'{0}', \n", addExtraItemPanel.ClientID);
            sb.AppendFormat("'{0}', \n", myUl.ClientID);
            sb.AppendFormat("'{0}', \n", SelectedItemBoxCssClass);
            sb.AppendFormat("'{0}', \n", SelectedItemCssClass);
            sb.AppendFormat("'{0}', \n", SelectedItemOnDeleteCssClass);
            sb.AppendFormat("'url({0})', \n", src);

            sb.Append(ItemSelected != null ? "'1', \n" : "'0', \n");

            sb.AppendFormat("'{0}', \n", postBackButton.ClientID);
            sb.Append(ItemDeleted != null ? "'1', \n" : "'0', \n");

            sb.AppendFormat("'{0}', \n", revPostBackButton.ClientID);
            sb.Append(Multiple ? "'1', \n" : "'0', \n");
            sb.Append(Insertable ? "'1', \n" : "'0', \n");

            sb.AppendFormat("'{0}', \n", string.IsNullOrEmpty(CustomJavascriptFunctionOnItemSelected)
                ? "-1" : CustomJavascriptFunctionOnItemSelected);

            sb.AppendFormat("'{0}', \n", string.IsNullOrEmpty(CustomJavascriptFunctionOnItemDeleted)
                ? "-1" : CustomJavascriptFunctionOnItemDeleted);

            sb.AppendFormat("'{0}' \n", ShowLastUsedWords ? LastUsedWordCount : 0);

            sb.Append(");");

            if (!Page.ClientScript.IsClientScriptBlockRegistered(GetType(), UniqueID))
                Page.ClientScript.RegisterStartupScript(GetType(), UniqueID, sb.ToString(), true);
        }

        private void SetJavaFunction()
        {
            var sb = new StringBuilder();
            Java();

            keyWordTextBox_AutoCompleteExtender.OnClientItemSelected =
                string.Format(
                    Multiple
                        ? "ctrl_{0}.SetSelectedValue"
                        : "ctrl_{0}.SetSelectedSingleValue", ClientID
                    );

            if (Insertable)
                ShowHideAddButtonFunctions();
            else
            {
                keyWordTextBox_AutoCompleteExtender.OnClientHidden = "";
                keyWordTextBox_AutoCompleteExtender.OnClientShown = "";
            }
            keyWordTextBox.Attributes.Add("onfocus"
                                          , string.Format("ctrl_{0}.SetUp(this);", ClientID));

            OptimizeJava(sb);

            if (!Page.ClientScript.IsClientScriptBlockRegistered(suggestorPanel.GetType(), UniqueID))
            {
                ScriptManager.RegisterClientScriptBlock(suggestorPanel, typeof(Panel), UniqueID, sb.ToString(), true);
            }

            keyWordTextBox_TextBoxWatermarkExtender.BehaviorID =
                    string.Format("ctrl_{0}_wext", ClientID);

        }

        private static void OptimizeJava(StringBuilder sb)
        {
            sb.Replace(" \n ", string.Empty);
        }

        #region JAVA FUNCTIONS

        private void ShowHideAddButtonFunctions()
        {
            var jv = string.Format("ctrl_{0}.addItemToNewItems(document.getElementById('{1}').value);"
                                      , ClientID, keyWordTextBox.ClientID);
            addExtraItemPanel.Attributes.Add("onclick", jv);

            if (Insertable)
            {
                keyWordTextBox_AutoCompleteExtender.OnClientHidden = string.Format("ctrl_{0}.ShowAddKey", ClientID);
                keyWordTextBox_AutoCompleteExtender.OnClientShown = string.Format("ctrl_{0}.HideAddKey", ClientID);
                keyWordTextBox_AutoCompleteExtender.OnClientItemOver = string.Format("ctrl_{0}.HideAddKey", ClientID);
                keyWordTextBox_AutoCompleteExtender.OnClientPopulated = string.Format("ctrl_{0}.HideAddKey", ClientID);
                keyWordTextBox_AutoCompleteExtender.OnClientItemOut = string.Format("ctrl_{0}.HideAddKey", ClientID);
            }
            else
            {
                keyWordTextBox_AutoCompleteExtender.OnClientHidden = "";
                keyWordTextBox_AutoCompleteExtender.OnClientShown = "";
                keyWordTextBox_AutoCompleteExtender.OnClientItemOver = "";
                keyWordTextBox_AutoCompleteExtender.OnClientPopulated = "";
                keyWordTextBox_AutoCompleteExtender.OnClientItemOut = "";
            }
        }

        #endregion

        private void GetLastUsedList()
        {
            try
            {
                LastUsedWordCount = LastUsedWordList.Count;
                liLiteral.Text = string.Empty;
                valuesHiddenField.Value = string.Empty;
                newValuesHiddenField.Value = string.Empty;

                if (LastUsedWordCount > 0)
                {
                    foreach (var item in LastUsedWordList)
                    {
                        liLiteral.Text +=
                            string.Format("<li id=\"ctrl_{0}_{1}\" class=\"{3}\"><a href='javascript:;' onclick=\"ctrl_{0}.deleteItem('ctrl_{0}_{1}'); return false; \">{2} <span style='float:right; display:block;'> X </span> </a></li>"
                            , ClientID, item.WordId, item.Text, SelectedItemCssClass);
                        if (string.IsNullOrEmpty(valuesHiddenField.Value))
                            valuesHiddenField.Value = "#";

                        valuesHiddenField.Value += string.Format("{0}#", item.WordId);
                    }

                }
            }
            catch (Exception ex)
            {
                LastUsedWordCount = 0;
                LastUsedWordList = new List<Word>();
                liLiteral.Text = string.Empty;

                throw new Exception("Error in LastUsedWord list process.", ex.InnerException);
            }
        }

        private void HideLastUsedWordList()
        {
            LastUsedWordCount = 0;
            LastUsedWordList = new List<Word>();
            liLiteral.Text = string.Empty;
        }

        private void ConfigureAutoCompleteExtender()
        {
            keyWordTextBox_AutoCompleteExtender.MinimumPrefixLength = MinimumPrefixLength;
            keyWordTextBox_AutoCompleteExtender.ContextKey = ContextKey;
            keyWordTextBox_AutoCompleteExtender.CompletionSetCount = CompletionSetCount;
            keyWordTextBox_TextBoxWatermarkExtender.WatermarkText = WatermarkText;
            keyWordTextBox_AutoCompleteExtender.ServiceMethod = ServiceMethod;
            keyWordTextBox_AutoCompleteExtender.ServicePath = ServicePath;
            keyWordTextBox_AutoCompleteExtender.CompletionSetCount = CompletionSetCount;
            keyWordTextBox_AutoCompleteExtender.CompletionInterval = CompletionInterval;
            keyWordTextBox_AutoCompleteExtender.BehaviorID = string.Format("bid_{0}", ClientID);

            keyWordTextBox_AutoCompleteExtender.OnClientPopulating = string.Format("ctrl_{0}.ShowImage", ClientID);
            keyWordTextBox_AutoCompleteExtender.OnClientPopulated = string.Format("ctrl_{0}.HideImage", ClientID);

            keyWordTextBox_AutoCompleteExtender.TargetControlID = keyWordTextBox.ID;
            keyWordTextBox_AutoCompleteExtender.CompletionListCssClass = ListCssClass;
            keyWordTextBox_AutoCompleteExtender.CompletionListItemCssClass = ListItemCssClass;
            keyWordTextBox_AutoCompleteExtender.CompletionListHighlightedItemCssClass = HighlightedItemCssClass;

            keyWordTextBox.CssClass = TextBoxCssClass;

            keyWordTextBox_TextBoxWatermarkExtender.WatermarkCssClass = WatermarkClass;

            keyWordTextBox_AutoCompleteExtender.FirstRowSelected = true;
            keyWordTextBox_AutoCompleteExtender.UseContextKey = true;
            keyWordTextBox_AutoCompleteExtender.ShowOnlyCurrentWordInCompletionListItem = true;
            keyWordTextBox_AutoCompleteExtender.DelimiterCharacters = ",";
            keyWordTextBox_AutoCompleteExtender.EnableCaching = true;

        }

        #region Control Texts

        private void SetTextsOfControl()
        {
            keyWordTextBox_TextBoxWatermarkExtender.Enabled = !string.IsNullOrEmpty(WatermarkText);
        }

        #endregion


        #endregion

        #region EVENTS

        void postBackButton_Click(object sender, EventArgs e)
        {
            ItemSelectedEvent(EventArgs.Empty);

            if (keyWordTextBox_TextBoxWatermarkExtender.Enabled)
                keyWordTextBox_TextBoxWatermarkExtender.Focus();
        }

        void revPostBackButton_Click(object sender, EventArgs e)
        {
            ItemDeletedEvent(EventArgs.Empty);

            if (keyWordTextBox_TextBoxWatermarkExtender.Enabled)
                keyWordTextBox_TextBoxWatermarkExtender.Focus();
        }

        #endregion

        #region CONTROL FUNS

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            SetTextsOfControl();

            if (ShowLastUsedWords)
                GetLastUsedList();
            else
                HideLastUsedWordList();

            EmbedJavaScriptFiles();
            EmbedCssFiles();

            SetJavaFunction();
        }



        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            GetAndClearValues();
            ConfigureAllControls();
        }

        private void ConfigureAllControls()
        {
            keyWordTextBox.Width = TotalWidth;
        }


        #endregion


        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            Page.RegisterRequiresControlState(this);
            Controls.Clear();

            base.CreateChildControls();

            suggestControlPlaceHolder.ID = ClientID + "aCControlPlaceHolder";
            clearLiteral1.Text = clearLiteral2.Text = clearLiteral3.Text = "<span style='clear:both; display:block; height:0px;'></span>";
            controlUpdatePanel.ID = ClientID + "acControlUpdatePanel";
            controlUpdatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
            controlUpdatePanel.ChildrenAsTriggers = false;

            suggestorPanel.ID = ClientID + "aCControlPanel";
            suggestorPanel.CssClass = "acc_cbox";

            slt = new Label { ID = ClientID + "slt" };

            valuesHiddenField.ID = ClientID + "valuesHiddenField";
            newValuesHiddenField.ID = ClientID + "newValuesHiddenField";

            ulContainerPanel.ID = ClientID + "ulContainerPanel";
            ulContainerPanel.Attributes.Add("style", "float:none;display: block; margin: 0px; padding: 0px;");

            myUl.ID = ClientID + "myUl";
            myUl.Attributes.Add("class", SelectedItemBoxCssClass);
            myUl.Attributes.Add("style", "list-style: none; padding: 0px; float: none; display: block; margin: 0px;");

            toolBoxPanel.ID = ClientID + "toolBoxPanel";
            toolBoxPanel.Attributes.Add("style", "float:none; display:block;");

            keyWordTextBox.ID = ClientID + "keyWordTextBox";
            keyWordTextBox.Attributes.Add("autocomplete", "off");
            keyWordTextBox.Attributes.Add("style", "padding: 2px 20px 2px 2px; border:none; margin:0px;");

            keyWordTextBox_TextBoxWatermarkExtender.ID = ClientID + "keyWordTextBox_TextBoxWatermarkExtender1";
            keyWordTextBox_TextBoxWatermarkExtender.Enabled = true;
            keyWordTextBox_TextBoxWatermarkExtender.TargetControlID = keyWordTextBox.ID;

            keyWordTextBox_AutoCompleteExtender.ID = ClientID + "keyWordTextBox_AutoCompleteExtender1";

            addExtraItemPanel.ID = ClientID + "addExtraItemPanel";
            addExtraItemPanel.CssClass = "acc_exstraItemContainer";
            addExtraItemPanel.Style.Add("display", "none");

            addTxtLabel.ID = ClientID + "addTxtLabel";

            postBackButton.ID = ClientID + "postBackButton";
            postBackButton.UseSubmitBehavior = true;
            postBackButton.Click += postBackButton_Click;

            postBackButton.Width = Unit.Parse("1px");
            postBackButton.Height = Unit.Parse("1px");
            postBackButton.Attributes.Add("style", "position: absolute; visibility: hidden;");


            revPostBackButton.ID = ClientID + "revPostBackButton";
            revPostBackButton.Click += revPostBackButton_Click;
            revPostBackButton.Width = Unit.Parse("1px");
            revPostBackButton.Height = Unit.Parse("1px");
            revPostBackButton.Attributes.Add("style", "position: absolute; visibility: hidden;");

            buttonsPanel.ID = ClientID + "buttonsPanel";

            suggestBoxPanel.ID = ClientID + "suggestBoxPanel";
            suggestBoxPanel.Attributes.Add("style", "float:none; display:block;");

            ConfigureAutoCompleteExtender();

            suggestControlPlaceHolder.Controls.Add(scriptsLiteral);
            suggestControlPlaceHolder.Controls.Add(suggestorPanel);
            suggestControlPlaceHolder.Controls.Add(keyboardLiteral);
            suggestorPanel.Controls.Add(valuesHiddenField);
            suggestorPanel.Controls.Add(newValuesHiddenField);
            suggestorPanel.Controls.Add(slt);
            suggestorPanel.Controls.Add(boxPanel);

            buttonsPanel.Controls.Add(postBackButton);
            buttonsPanel.Controls.Add(revPostBackButton);
            buttonsPanel.Controls.Add(clearLiteral1);
            suggestorPanel.Controls.Add(buttonsPanel);
            suggestorPanel.Controls.Add(clearLiteral2);

            suggestBoxPanel.Controls.Add(keyWordTextBox);

            boxPanel.Controls.Add(ulContainerPanel);
            boxPanel.Controls.Add(toolBoxPanel);
            boxPanel.Controls.Add(clearLiteral3);

            ulContainerPanel.Controls.Add(myUl);
            myUl.Controls.Add(liLiteral);
            toolBoxPanel.Controls.Add(suggestBoxPanel);
            toolBoxPanel.Controls.Add(keyWordTextBox_AutoCompleteExtender);
            toolBoxPanel.Controls.Add(keyWordTextBox_TextBoxWatermarkExtender);
            toolBoxPanel.Controls.Add(addExtraItemPanel);

            addExtraItemPanel.Controls.Add(addTxtLabel);

            controlUpdatePanel.ContentTemplateContainer.Controls.Add(suggestControlPlaceHolder);

            controlContainerPanel.Controls.Add(controlUpdatePanel);
            Controls.Add(controlContainerPanel);
        
        }
    }
}
